﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace NRemedy
{

    public class PropertyInfoAndARAttribute
    {
        public PropertyInfo Property { get; set; }
        public ARFieldAttribute ARAttribute { get; set; }
    }


    public class BinderHelper
    {
        public static Dictionary<string, List<PropertyInfoAndARAttribute>> _maps = new Dictionary<string, List<PropertyInfoAndARAttribute>>();


        /// <summary>
        /// 
        /// </summary>
        /// <param name="mType"></param>
        /// <param name="IsBinder">is the ar convert to the .net</param>
        /// <returns></returns>
        public static string GetClassName(Type mType, bool IsBinder)
        {
            var keyName = "Copy_";
            keyName += mType.FullName.Replace(".", "_");
            keyName += "Binder";
            if (IsBinder)
            {
                keyName += "_TypeIsBinder";
            }
            else
            {
                keyName += "_TypeIsUnBinder";
            }
            return keyName;
        }
        public static List<PropertyInfoAndARAttribute> GetPropertyInfos(Type mType, bool IsBinder)
        {
            List<PropertyInfoAndARAttribute> properties;
            string name = GetClassName(mType,IsBinder);


            ARFieldAttribute fieldAttribute = null;

            lock (_maps)
            {
                if (!_maps.TryGetValue(name, out properties))
                {
                    PropertyInfo[] propertiesTemp;
                    if (IsBinder)
                    {
                        propertiesTemp = mType.GetProperties
                            //binder need at least writeable property
                (BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance);
                        foreach (var item in propertiesTemp)
                        {
                            fieldAttribute = GetARAttributeFieldForProperty(item, ModelBinderAccessLevel.OnlyBind);
                            if (fieldAttribute != null) properties.Add(
                                new PropertyInfoAndARAttribute
                                {
                                    Property = item,
                                    ARAttribute = fieldAttribute
                                });
                        }
                    }
                    else
                    {
                        propertiesTemp = mType.GetProperties
                            //unbinder need at least readable property
                (BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance);
                        foreach (var item in propertiesTemp)
                        {
                            fieldAttribute = GetARAttributeFieldForProperty(item, ModelBinderAccessLevel.OnlyUnBind);
                            if (fieldAttribute != null) properties.Add(
                                 new PropertyInfoAndARAttribute
                                 {
                                     Property = item,
                                     ARAttribute = fieldAttribute
                                 });
                        }
                    }
                    _maps.Add(name, properties);
                }
            }
            return properties;
        }


        public static ARFieldAttribute GetARAttributeFieldForProperty(PropertyInfo pi, ModelBinderAccessLevel accessLevel)
        {
            if (pi == null)
                throw new ArgumentNullException("pi");
            var attrs = pi.GetCustomAttributes(typeof(ARFieldAttribute), false);
            if (attrs.Length > 1)
                throw new CustomAttributeFormatException(
                    string.Format("Mutiple ARFieldAttribute is found on Property : {0}.", pi.Name));
            if (attrs.Length == 0) return null;
            ARFieldAttribute attribute = attrs.First() as ARFieldAttribute;
            if (attribute.DatabaseID == 0)
                throw new CustomAttributeFormatException(
                    string.Format("DatabaseID of ARFieldAttribute on Property : {0} is missing.", pi.Name));
            if (attribute.DataType == ARType.None)
                throw new CustomAttributeFormatException(
                    string.Format("DataType of ARFieldAttribute on Property : {0} cannot be null.", pi.Name));
            if ((attribute.BinderAccess & accessLevel) == accessLevel)
                return attribute;
            else
                return null;
        }

    }
}
